﻿using System;
using System.Threading;
using System.Threading.Tasks;
using System.IO.Pipelines;
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Internal;
using System.Linq;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Primitives;
using System.Collections;
using System.Buffers;
using System.Net;
using System.Diagnostics;

namespace JTActiveSafety.Gateway.Internal
{
    //public class KestrelServer : IServer
    //{
    //    private readonly List<ITransport> _transports = new List<ITransport>();
    //    private readonly Heartbeat _heartbeat;
    //    private readonly IServerAddressesFeature _serverAddresses;
    //    private readonly ITransportFactory _transportFactory;

    //    private bool _hasStarted;
    //    private int _stopped;

    //    public KestrelServer(IOptions<KestrelServerOptions> options, ITransportFactory transportFactory, ILoggerFactory loggerFactory)
    //        : this(transportFactory, CreateServiceContext(options, loggerFactory))
    //    {
    //    }

    //    // For testing
    //    internal KestrelServer(ITransportFactory transportFactory, ServiceContext serviceContext)
    //    {
    //        if (transportFactory == null)
    //        {
    //            throw new ArgumentNullException(nameof(transportFactory));
    //        }

    //        _transportFactory = transportFactory;
    //        ServiceContext = serviceContext;

    //        var frameHeartbeatManager = new FrameHeartbeatManager(serviceContext.ConnectionManager);
    //        _heartbeat = new Heartbeat(
    //            new IHeartbeatHandler[] { serviceContext.DateHeaderValueManager, frameHeartbeatManager },
    //            serviceContext.SystemClock, Trace);

    //        Features = new FeatureCollection();
    //        _serverAddresses = new ServerAddressesFeature();
    //        Features.Set(_serverAddresses);
    //    }

    //    private static ServiceContext CreateServiceContext(IOptions<KestrelServerOptions> options, ILoggerFactory loggerFactory)
    //    {
    //        if (options == null)
    //        {
    //            throw new ArgumentNullException(nameof(options));
    //        }
    //        if (loggerFactory == null)
    //        {
    //            throw new ArgumentNullException(nameof(loggerFactory));
    //        }

    //        var serverOptions = options.Value ?? new KestrelServerOptions();
    //        var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Server.Kestrel");
    //        var trace = new KestrelTrace(logger);
    //        var connectionManager = new FrameConnectionManager(trace);

    //        var systemClock = new SystemClock();
    //        var dateHeaderValueManager = new DateHeaderValueManager(systemClock);

    //        // TODO: This logic will eventually move into the IConnectionHandler<T> and off
    //        // the service context once we get to https://github.com/aspnet/KestrelHttpServer/issues/1662
    //        IThreadPool threadPool = null;
    //        switch (serverOptions.ApplicationSchedulingMode)
    //        {
    //            case SchedulingMode.Default:
    //            case SchedulingMode.ThreadPool:
    //                threadPool = new LoggingThreadPool(trace);
    //                break;
    //            case SchedulingMode.Inline:
    //                threadPool = new InlineLoggingThreadPool(trace);
    //                break;
    //            default:
    //                throw new NotSupportedException($"Unknown transport mode {serverOptions.ApplicationSchedulingMode}");
    //        }

    //        return new ServiceContext
    //        {
    //            Log = trace,
    //            HttpParserFactory = frameParser => new HttpParser<FrameAdapter>(frameParser.Frame.ServiceContext.Log),
    //            ThreadPool = threadPool,
    //            SystemClock = systemClock,
    //            DateHeaderValueManager = dateHeaderValueManager,
    //            ConnectionManager = connectionManager,
    //            ServerOptions = serverOptions
    //        };
    //    }

    //    public IFeatureCollection Features { get; }

    //    public KestrelServerOptions Options => ServiceContext.ServerOptions;

    //    private ServiceContext ServiceContext { get; }

    //    private IKestrelTrace Trace => ServiceContext.Log;

    //    private FrameConnectionManager ConnectionManager => ServiceContext.ConnectionManager;

    //    public async Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken)
    //    {
    //        try
    //        {
    //            if (!BitConverter.IsLittleEndian)
    //            {
    //                throw new PlatformNotSupportedException("Kestrel does not support big-endian architectures.");
    //            }

    //            ValidateOptions();

    //            if (_hasStarted)
    //            {
    //                // The server has already started and/or has not been cleaned up yet
    //                throw new InvalidOperationException("Server has already started.");
    //            }
    //            _hasStarted = true;
    //            _heartbeat.Start();

    //            async Task OnBind(ListenOptions endpoint)
    //            {
    //                var connectionHandler = new ConnectionHandler<TContext>(endpoint, ServiceContext, application);
    //                var transport = _transportFactory.Create(endpoint, connectionHandler);
    //                _transports.Add(transport);

    //                await transport.BindAsync().ConfigureAwait(false);
    //            }

    //            await AddressBinder.BindAsync(_serverAddresses, Options.ListenOptions, Trace, OnBind).ConfigureAwait(false);
    //        }
    //        catch (Exception ex)
    //        {
    //            Trace.LogCritical(0, ex, "Unable to start Kestrel.");
    //            Dispose();
    //            throw;
    //        }
    //    }

    //    // Graceful shutdown if possible
    //    public async Task StopAsync(CancellationToken cancellationToken)
    //    {
    //        if (Interlocked.Exchange(ref _stopped, 1) == 1)
    //        {
    //            return;
    //        }

    //        var tasks = new Task[_transports.Count];
    //        for (int i = 0; i < _transports.Count; i++)
    //        {
    //            tasks[i] = _transports[i].UnbindAsync();
    //        }
    //        await Task.WhenAll(tasks).ConfigureAwait(false);

    //        if (!await ConnectionManager.CloseAllConnectionsAsync(cancellationToken).ConfigureAwait(false))
    //        {
    //            Trace.NotAllConnectionsClosedGracefully();

    //            if (!await ConnectionManager.AbortAllConnectionsAsync().ConfigureAwait(false))
    //            {
    //                Trace.NotAllConnectionsAborted();
    //            }
    //        }

    //        for (int i = 0; i < _transports.Count; i++)
    //        {
    //            tasks[i] = _transports[i].StopAsync();
    //        }
    //        await Task.WhenAll(tasks).ConfigureAwait(false);

    //        _heartbeat.Dispose();
    //    }

    //    // Ungraceful shutdown
    //    public void Dispose()
    //    {
    //        var cancelledTokenSource = new CancellationTokenSource();
    //        cancelledTokenSource.Cancel();
    //        StopAsync(cancelledTokenSource.Token).GetAwaiter().GetResult();
    //    }

    //    private void ValidateOptions()
    //    {
    //        if (Options.Limits.MaxRequestBufferSize.HasValue &&
    //            Options.Limits.MaxRequestBufferSize < Options.Limits.MaxRequestLineSize)
    //        {
    //            throw new InvalidOperationException(
    //                $"Maximum request buffer size ({Options.Limits.MaxRequestBufferSize.Value}) must be greater than or equal to maximum request line size ({Options.Limits.MaxRequestLineSize}).");
    //        }

    //        if (Options.Limits.MaxRequestBufferSize.HasValue &&
    //            Options.Limits.MaxRequestBufferSize < Options.Limits.MaxRequestHeadersTotalSize)
    //        {
    //            throw new InvalidOperationException(
    //                $"Maximum request buffer size ({Options.Limits.MaxRequestBufferSize.Value}) must be greater than or equal to maximum request headers size ({Options.Limits.MaxRequestHeadersTotalSize}).");
    //        }
    //    }
    //}


    internal class KestrelServerImpl : IServer
    {
        private readonly ServerAddressesFeature _serverAddresses;
        private readonly TransportManager _transportManager;
        private readonly IConnectionListenerFactory _transportFactory;
        //private readonly IMultiplexedConnectionListenerFactory _multiplexedTransportFactory;

        private readonly SemaphoreSlim _bindSemaphore = new SemaphoreSlim(initialCount: 1);
        private bool _hasStarted;
        private int _stopping;
        private readonly CancellationTokenSource _stopCts = new CancellationTokenSource();
        private readonly TaskCompletionSource _stoppedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);

        private IDisposable _configChangedRegistration;

        public KestrelServerImpl(
            IOptions<KestrelServerOptions> options,
            IEnumerable<IConnectionListenerFactory> transportFactories,
            ILoggerFactory loggerFactory)
            : this(transportFactories, CreateServiceContext(options, loggerFactory))
        {
        }

        // For testing
        internal KestrelServerImpl(IConnectionListenerFactory transportFactory, ServiceContext serviceContext)
            : this(new[] { transportFactory },   serviceContext)
        {
        }

        // For testing
        internal KestrelServerImpl(
            IEnumerable<IConnectionListenerFactory> transportFactories,
            ServiceContext serviceContext)
        {
            if (transportFactories == null)
            {
                throw new ArgumentNullException(nameof(transportFactories));
            }

            _transportFactory = transportFactories?.LastOrDefault();
 
            if (_transportFactory == null)
            {
                throw new InvalidOperationException("IConnectionListenerFactory Not Found.");
            }

            ServiceContext = serviceContext;

            Features = new FeatureCollection();
            _serverAddresses = new ServerAddressesFeature();
            Features.Set<IServerAddressesFeature>(_serverAddresses);

            _transportManager = new TransportManager(_transportFactory, ServiceContext);

            //HttpCharacters.Initialize();
        }

        private static ServiceContext CreateServiceContext(IOptions<KestrelServerOptions> options, ILoggerFactory loggerFactory)
        {
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }
            if (loggerFactory == null)
            {
                throw new ArgumentNullException(nameof(loggerFactory));
            }

            var serverOptions = options.Value ?? new KestrelServerOptions();
            var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Server.Kestrel");
            var trace = new KestrelTrace(logger);
            var connectionManager = new ConnectionManager(
                trace,
                serverOptions.Limits.MaxConcurrentUpgradedConnections);

            var heartbeatManager = new HeartbeatManager(connectionManager);
            //var dateHeaderValueManager = new DateHeaderValueManager();

            var heartbeat = new Heartbeat(
                new IHeartbeatHandler[] { heartbeatManager },
                new SystemClock(),
                DebuggerWrapper.Singleton,
                trace);

            return new ServiceContext
            {
                Log = trace,
                 
                Scheduler = PipeScheduler.ThreadPool,
                SystemClock = heartbeatManager,
 
                ConnectionManager = connectionManager,
                Heartbeat = heartbeat,
                ServerOptions = serverOptions,
            };
        }

        public IFeatureCollection Features { get; }

        public KestrelServerOptions Options => ServiceContext.ServerOptions;

        private ServiceContext ServiceContext { get; }

        private IKestrelTrace Trace => ServiceContext.Log;

        private AddressBindContext AddressBindContext { get; set; }

        public async Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken)
        {
            try
            {
                if (!BitConverter.IsLittleEndian)
                {
                    throw new PlatformNotSupportedException("Big Endian Not Supported.");
                }

                ValidateOptions();

                if (_hasStarted)
                {
                    // The server has already started and/or has not been cleaned up yet
                    throw new InvalidOperationException("Server Already Started");
                }
                _hasStarted = true;

                ServiceContext.Heartbeat?.Start();

                async Task OnBind(ListenOptions options)
                {
                INVESTIGATE: For some reason, MsQuic needs to bind before
                sockets for it to successfully listen.It also seems racy.
                    if ((options.Protocols & HttpProtocols.Http3) == HttpProtocols.Http3)
                        {
                            if (_multiplexedTransportFactory is null)
                            {
                                throw new InvalidOperationException($"Cannot start HTTP/3 server if no {nameof(IMultiplexedConnectionListenerFactory)} is registered.");
                            }

                            options.UseHttp3Server(ServiceContext, application, options.Protocols);
                            var multiplexedConnectionDelegate = ((IMultiplexedConnectionBuilder)options).Build();

                            // Add the connection limit middleware
                            multiplexedConnectionDelegate = EnforceConnectionLimit(multiplexedConnectionDelegate, Options.Limits.MaxConcurrentConnections, Trace);

                            options.EndPoint = await _transportManager.BindAsync(options.EndPoint, multiplexedConnectionDelegate, options.EndpointConfig).ConfigureAwait(false);
                        }

                    // Add the HTTP middleware as the terminal connection middleware
                    if ((options.Protocols & HttpProtocols.Http1) == HttpProtocols.Http1
                        || (options.Protocols & HttpProtocols.Http2) == HttpProtocols.Http2
                        || options.Protocols == HttpProtocols.None) // TODO a test fails because it doesn't throw an exception in the right place
                                                                    // when there is no HttpProtocols in KestrelServer, can we remove/change the test?
                    {
                        if (_transportFactory is null)
                        {
                            throw new InvalidOperationException($"Cannot start HTTP/1.x or HTTP/2 server if no {nameof(IConnectionListenerFactory)} is registered.");
                        }

                        options.UseHttpServer(ServiceContext, application, options.Protocols);
                        //var connectionDelegate = options.Build();

                        // Add the connection limit middleware
                        //connectionDelegate = EnforceConnectionLimit(connectionDelegate, Options.Limits.MaxConcurrentConnections, Trace);

                        //options.EndPoint = await _transportManager.BindAsync(options.EndPoint, connectionDelegate, options.EndpointConfig).ConfigureAwait(false);
                    }

                    options.UseHttpServer(ServiceContext, application, options.Protocols);
                    var connectionDelegate = options.Build();

                    // Add the connection limit middleware
                    connectionDelegate = EnforceConnectionLimit(connectionDelegate, Options.Limits.MaxConcurrentConnections, Trace);
                    options.EndPoint = await _transportManager.BindAsync(options.EndPoint, connectionDelegate, options.EndpointConfig).ConfigureAwait(false);
                }

                AddressBindContext = new AddressBindContext
                {
                    ServerAddressesFeature = _serverAddresses,
                    ServerOptions = Options,
                    Logger = Trace,
                    CreateBinding = OnBind,
                };

                await BindAsync(cancellationToken).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                Trace.LogCritical(0, ex, "Unable to start Kestrel.");
                Dispose();
                throw;
            }
        }

        // Graceful shutdown if possible
        public async Task StopAsync(CancellationToken cancellationToken)
        {
            if (Interlocked.Exchange(ref _stopping, 1) == 1)
            {
                await _stoppedTcs.Task.ConfigureAwait(false);
                return;
            }

            _stopCts.Cancel();

            // Don't use cancellationToken when acquiring the semaphore. Dispose calls this with a pre-canceled token.
            await _bindSemaphore.WaitAsync().ConfigureAwait(false);

            try
            {
                await _transportManager.StopAsync(cancellationToken).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                _stoppedTcs.TrySetException(ex);
                throw;
            }
            finally
            {
                ServiceContext.Heartbeat?.Dispose();
                _configChangedRegistration?.Dispose();
                _stopCts.Dispose();
                _bindSemaphore.Release();
            }

            _stoppedTcs.TrySetResult();
        }

        // Ungraceful shutdown
        public void Dispose()
        {
            StopAsync(new CancellationToken(canceled: true)).GetAwaiter().GetResult();
        }

        private async Task BindAsync(CancellationToken cancellationToken)
        {
            await _bindSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);

            try
            {
                if (_stopping == 1)
                {
                    throw new InvalidOperationException("Kestrel has already been stopped.");
                }

                IChangeToken reloadToken = null;

                _serverAddresses.InternalCollection.PreventPublicMutation();

                if (Options.ConfigurationLoader?.ReloadOnChange == true && (!_serverAddresses.PreferHostingUrls || _serverAddresses.InternalCollection.Count == 0))
                {
                    reloadToken = Options.ConfigurationLoader.Configuration.GetReloadToken();
                }

                Options.ConfigurationLoader?.Load();

                //await AddressBinder.BindAsync(Options.ListenOptions, AddressBindContext).ConfigureAwait(false);
                _configChangedRegistration = reloadToken?.RegisterChangeCallback(async state => await ((KestrelServerImpl)state).RebindAsync(), this);
            }
            finally
            {
                _bindSemaphore.Release();
            }
        }

        private async Task RebindAsync()
        {
            await _bindSemaphore.WaitAsync();

            IChangeToken reloadToken = null;

            try
            {
                if (_stopping == 1)
                {
                    return;
                }

                //reloadToken = Options.ConfigurationLoader.Configuration.GetReloadToken();
                //var (endpointsToStop, endpointsToStart) = Options.ConfigurationLoader.Reload();

                //Trace.LogDebug("Config reload token fired. Checking for changes...");

                //if (endpointsToStop.Count > 0)
                //{
                //    var urlsToStop = endpointsToStop.Select(lo => lo.EndpointConfig.Url ?? "<unknown>");
                //    Trace.LogInformation("Config changed. Stopping the following endpoints: '{endpoints}'", string.Join("', '", urlsToStop));

                //    // 5 is the default value for WebHost's "shutdownTimeoutSeconds", so use that.
                //    using var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
                //    using var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(_stopCts.Token, timeoutCts.Token);

                //    // TODO: It would be nice to start binding to new endpoints immediately and reconfigured endpoints as soon
                //    // as the unbinding finished for the given endpoint rather than wait for all transports to unbind first.
                //    var configsToStop = endpointsToStop.Select(lo => lo.EndpointConfig).ToList();
                //    await _transportManager.StopEndpointsAsync(configsToStop, combinedCts.Token).ConfigureAwait(false);

                //    foreach (var listenOption in endpointsToStop)
                //    {
                //        Options.OptionsInUse.Remove(listenOption);
                //        _serverAddresses.InternalCollection.Remove(listenOption.GetDisplayName());
                //    }
                //}

                //if (endpointsToStart.Count > 0)
                //{
                //    var urlsToStart = endpointsToStart.Select(lo => lo.EndpointConfig.Url ?? "<unknown>");
                //    Trace.LogInformation("Config changed. Starting the following endpoints: '{endpoints}'", string.Join("', '", urlsToStart));

                //    foreach (var listenOption in endpointsToStart)
                //    {
                //        try
                //        {
                //            // TODO: This should probably be canceled by the _stopCts too, but we don't currently support bind cancellation even in StartAsync().
                //            await listenOption.BindAsync(AddressBindContext).ConfigureAwait(false);
                //        }
                //        catch (Exception ex)
                //        {
                //            Trace.LogCritical(0, ex, "Unable to bind to '{url}' on config reload.", listenOption.EndpointConfig.Url ?? "<unknown>");
                //        }
                //    }
                //}
            }
            catch (Exception ex)
            {
                Trace.LogCritical(0, ex, "Unable to reload configuration.");
            }
            finally
            {
                _configChangedRegistration = reloadToken?.RegisterChangeCallback(async state => await ((KestrelServerImpl)state).RebindAsync(), this);
                _bindSemaphore.Release();
            }
        }

        private void ValidateOptions()
        {
            //在这里进行配置参数检查
            //if (Options.Limits.MaxRequestBufferSize.HasValue &&
            //    Options.Limits.MaxRequestBufferSize < Options.Limits.MaxRequestLineSize)
            //{
            //    throw new InvalidOperationException(
            //        CoreStrings.FormatMaxRequestBufferSmallerThanRequestLineBuffer(Options.Limits.MaxRequestBufferSize.Value, Options.Limits.MaxRequestLineSize));
            //}

            //if (Options.Limits.MaxRequestBufferSize.HasValue &&
            //    Options.Limits.MaxRequestBufferSize < Options.Limits.MaxRequestHeadersTotalSize)
            //{
            //    throw new InvalidOperationException(
            //        CoreStrings.FormatMaxRequestBufferSmallerThanRequestHeaderBuffer(Options.Limits.MaxRequestBufferSize.Value, Options.Limits.MaxRequestHeadersTotalSize));
            //}
        }

        private static ConnectionDelegate EnforceConnectionLimit(ConnectionDelegate innerDelegate, long? connectionLimit, IKestrelTrace trace)
        {
            if (!connectionLimit.HasValue)
            {
                return innerDelegate;
            }

            return new ConnectionLimitMiddleware<ConnectionContext>(c => innerDelegate(c), connectionLimit.Value, trace).OnConnectionAsync;
        }

    }
    public class FeatureCollection : IFeatureCollection
    {
        private static KeyComparer FeatureKeyComparer = new KeyComparer();
        private readonly IFeatureCollection? _defaults;
        private IDictionary<Type, object>? _features;
        private volatile int _containerRevision;

        public FeatureCollection()
        {
        }

        public FeatureCollection(IFeatureCollection defaults)
        {
            _defaults = defaults;
        }

        public virtual int Revision
        {
            get { return _containerRevision + (_defaults?.Revision ?? 0); }
        }

        public bool IsReadOnly { get { return false; } }

        public object? this[Type key]
        {
            get
            {
                if (key == null)
                {
                    throw new ArgumentNullException(nameof(key));
                }

                object? result;
                return _features != null && _features.TryGetValue(key, out result) ? result : _defaults?[key];
            }
            set
            {
                if (key == null)
                {
                    throw new ArgumentNullException(nameof(key));
                }

                if (value == null)
                {
                    if (_features != null && _features.Remove(key))
                    {
                        _containerRevision++;
                    }
                    return;
                }

                if (_features == null)
                {
                    _features = new Dictionary<Type, object>();
                }
                _features[key] = value;
                _containerRevision++;
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public IEnumerator<KeyValuePair<Type, object>> GetEnumerator()
        {
            if (_features != null)
            {
                foreach (var pair in _features)
                {
                    yield return pair;
                }
            }

            if (_defaults != null)
            {
                // Don't return features masked by the wrapper.
                foreach (var pair in _features == null ? _defaults : _defaults.Except(_features, FeatureKeyComparer))
                {
                    yield return pair;
                }
            }
        }

        public TFeature? Get<TFeature>()
        {
            return (TFeature?)this[typeof(TFeature)];
        }

        public void Set<TFeature>(TFeature instance)
        {
            this[typeof(TFeature)] = instance;
        }

        private class KeyComparer : IEqualityComparer<KeyValuePair<Type, object>>
        {
            public bool Equals(KeyValuePair<Type, object> x, KeyValuePair<Type, object> y)
            {
                return x.Key.Equals(y.Key);
            }

            public int GetHashCode(KeyValuePair<Type, object> obj)
            {
                return obj.Key.GetHashCode();
            }
        }
    }
    internal class HeartbeatManager : IHeartbeatHandler, ISystemClock
    {
        private readonly ConnectionManager _connectionManager;
        private readonly Action<KestrelConnection> _walkCallback;
        private DateTimeOffset _now;
        private long _nowTicks;

        public HeartbeatManager(ConnectionManager connectionManager)
        {
            _connectionManager = connectionManager;
            _walkCallback = WalkCallback;
        }

        public DateTimeOffset UtcNow => new DateTimeOffset(UtcNowTicks, TimeSpan.Zero);

        public long UtcNowTicks => Volatile.Read(ref _nowTicks);

        public DateTimeOffset UtcNowUnsynchronized => _now;

        public void OnHeartbeat(DateTimeOffset now)
        {
            _now = now;
            Volatile.Write(ref _nowTicks, now.Ticks);

            _connectionManager.Walk(_walkCallback);
        }

        private void WalkCallback(KestrelConnection connection)
        {
            connection.TickHeartbeat();
        }
    }

    internal static class HttpConnectionBuilderExtensions
    {
        public static IConnectionBuilder UseHttpServer<TContext>(this IConnectionBuilder builder, ServiceContext serviceContext, IHttpApplication<TContext> application, TerminalProtocols protocols)
        {
            var middleware = new HttpConnectionMiddleware<TContext>(serviceContext, application, protocols);
            return builder.Use(next =>
            {
                return middleware.OnConnectionAsync;
            });
        }
    }

    //我们需要的是终端连接，非http 改造它
    internal class HttpConnectionMiddleware<TContext>
    {
        private readonly ServiceContext _serviceContext;
        private readonly IHttpApplication<TContext> _application;
        private readonly TerminalProtocols _endpointDefaultProtocols;

        public HttpConnectionMiddleware(ServiceContext serviceContext, IHttpApplication<TContext> application, TerminalProtocols protocols)
        {
            _serviceContext = serviceContext;
            _application = application;
            _endpointDefaultProtocols = protocols;
        }

        public Task OnConnectionAsync(ConnectionContext connectionContext)
        {
            var memoryPoolFeature = connectionContext.Features.Get<IMemoryPoolFeature>();

            var httpConnectionContext = new HttpConnectionContext
            {
                ConnectionId = connectionContext.ConnectionId,
                ConnectionContext = connectionContext,
                Protocols = connectionContext.Features.Get<HttpProtocolsFeature>()?.HttpProtocols ?? _endpointDefaultProtocols,
                ServiceContext = _serviceContext,
                ConnectionFeatures = connectionContext.Features,
                MemoryPool = memoryPoolFeature?.MemoryPool ?? System.Buffers.MemoryPool<byte>.Shared,
                Transport = connectionContext.Transport,
                LocalEndPoint = connectionContext.LocalEndPoint as IPEndPoint,
                RemoteEndPoint = connectionContext.RemoteEndPoint as IPEndPoint
            };

            var connection = new HttpConnection(httpConnectionContext);

            return connection.ProcessRequestsAsync(_application);
        }
    }

    
}